package edu.ntu.cloudroid.xacml.combining;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sun.xacml.AbstractPolicy;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.MatchResult;
import com.sun.xacml.Obligation;
import com.sun.xacml.attr.IntegerAttribute;
import com.sun.xacml.combine.PolicyCombiningAlgorithm;
import com.sun.xacml.ctx.Attribute;
import com.sun.xacml.ctx.Result;
import com.sun.xacml.ctx.Status;

import edu.ntu.cloudroid.xacml.XACMLException;
import edu.ntu.cloudroid.xacml.XACMLStrings;


/**
 * We're return union of all policies,
 * by returning all obligations
 */
public class ClouDroidPolicyCombiningAlgorithm extends PolicyCombiningAlgorithm
{
   private static final Logger logger = Logger
         .getLogger(ClouDroidPolicyCombiningAlgorithm.class.getName());

   public ClouDroidPolicyCombiningAlgorithm() throws URISyntaxException
   {
      super(new URI(XACMLStrings.ALG_ClouDroidAlgorithm));
   }

   @SuppressWarnings({ "rawtypes", "unchecked" })
   @Override
   public Result combine(EvaluationCtx context, List policies)
   {		
      boolean atLeastOneError = false;
      boolean atLeastOneDeny = false;
      boolean atLeastOnePermit = false;
      Set<Obligation> denyObligations = new HashSet<Obligation>();
      Set<Obligation> allObligations = new HashSet<Obligation>();
      Status firstIndeterminateStatus = null;
      Iterator it = policies.iterator();

      while (it.hasNext()) {
         AbstractPolicy policy = (AbstractPolicy) (it.next());
         // make sure that the policy matches the context
         MatchResult match = policy.match(context);         
         if (match.getResult() == MatchResult.INDETERMINATE) {
            atLeastOneError = true;

            // keep track of the first error, regardless of cause
            if (firstIndeterminateStatus == null)
               firstIndeterminateStatus = match.getStatus();
         } else if (match.getResult() == MatchResult.MATCH) {        	 
            // now we evaluate the policy
            Result result = policy.evaluate(context);
            int effect = result.getDecision();

            // this is a little different from DenyOverrides...
            if (effect == Result.DECISION_PERMIT) {            	
               Set<Obligation> currentObligations = result.getObligations();                              
               // Put all obligations together. We assume that only one Obligation per Result
               for (Obligation obl : currentObligations){
            	   allObligations.add(obl);
               }
               atLeastOnePermit = true;
            		   
            }

            if (effect == Result.DECISION_DENY) {
               atLeastOneDeny = true;
               denyObligations.addAll(result.getObligations());
            } else if (effect == Result.DECISION_INDETERMINATE) {
               atLeastOneError = true;
               // keep track of the first error, regardless of cause
               if (firstIndeterminateStatus == null)
                  firstIndeterminateStatus = result.getStatus();
            }
         }
      }     
      if (atLeastOnePermit)
         return new Result(Result.DECISION_PERMIT, context.getResourceId()
               .encode(), allObligations);

      // if we got a DENY, return it
      if (atLeastOneDeny)
         return new Result(Result.DECISION_DENY, context.getResourceId()
               .encode(), denyObligations);

      // if we got an INDETERMINATE, return it
      if (atLeastOneError)
         return new Result(Result.DECISION_INDETERMINATE,
               firstIndeterminateStatus, context.getResourceId().encode());

      // if we got here, then nothing applied to us
      return new Result(Result.DECISION_NOT_APPLICABLE, context.getResourceId()
            .encode());
   }  

}
